import { Inject, Injectable } from '@nestjs/common';
import { CreateChatDto } from './dto/create-chat.dto';
import { UpdateChatDto } from './dto/update-chat.dto';
import { RedisClientType } from 'redis';
import { BaiDuAiService } from 'src/bai-du-ai/bai-du-ai.service';
import { CheckUserAnswerIsOk } from './utils/chatCheck';
import { Observable } from 'rxjs';
import { modelDataDealMethodsMap } from './utils/modelResponseDeal';
import { generateSpeech } from './utils/textGenAudio';
import { ConfigService } from '@nestjs/config';
import axios from 'axios';
import * as fs from "fs"
import * as path from "path";

@Injectable()
export class ChatService {
  @Inject(ConfigService)
  private configService: ConfigService;

  @Inject('RedisClient')
  private redisClient: RedisClientType;

  @Inject('PromoptWords')
  private promoptWords: String;

  @Inject(BaiDuAiService)
  private baiDuAiService: BaiDuAiService;

  async getChatHistory(userId: number) {
    const userChatKey = `userid_${userId}`
    const userHistory = await this.redisClient.get(userChatKey)
    if (userHistory) {
      const historyArray = JSON.parse(userHistory) as Array<any>
      return historyArray.slice(2)
    } else {
      return []
    }
  }

  async create(createChatDto: CreateChatDto) {
    const messageList = [] // 内存消息空间
    const baiduURL = 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro'
    const userChatKey = `userid_${createChatDto.userId}` // 使用用户 ID 作为 Redis 键
    // redisHistoryChat 获取用户的历史聊天记录
    const redisHistoryChat = await this.redisClient.get(userChatKey)

    if (redisHistoryChat) {
      // 非第一次聊天，将用户的聊天记录缓存到 Redis
      messageList.push(...JSON.parse(redisHistoryChat)) // 初始化历史消息
      messageList.push({ role: 'user', content: createChatDto.promote }) // 表示不是第一次聊天，调用代表用户有回答
    } else {
      // 第一次聊天，用户此时只是发起对话，忽略 user方 对话内容
      messageList.push({ role: 'assistant', content: this.promoptWords }) // 初始化提示词消息)
      messageList.push({ role: 'user', content: "您好，我已经准备好开始测评了,请一个一个的来提问我问题吧!" }) // 初始化提示词消息)
    }

    const res = await this.baiDuAiService.callBaiduAiApi(baiduURL, {
      messages: messageList
    })

    if (res?.result) {
      messageList.push({
        role: 'assistant', content: res.result
      })
      // pureMsg 代表过滤标签后的回答，isOk 代表回答是否正确
      const [pureMsg, isOk] = CheckUserAnswerIsOk(res.result)
      if (!isOk) {
        // 告警处理
        console.log(`告警处理,用户${createChatDto.userId}的回答异常:${pureMsg}`)
      }
      // 将用户的聊天记录缓存到 Redis
      this.redisClient.set(userChatKey, JSON.stringify(messageList));
      return pureMsg;
    } else {
      return {
        result: '网络繁忙，请稍后再试'
      }
    }

  }

  async createV2(createChatDto: CreateChatDto) {
    try {
      const messageList = []
      const userChatKey = `userid_${createChatDto.userId}`
      let assistantTplMessage = ''
      const redisHistoryChat = await this.redisClient.get(userChatKey);

      if (redisHistoryChat) {
        messageList.push(...JSON.parse(redisHistoryChat));
        messageList.push({ role: 'user', content: createChatDto.promote });
      } else {
        messageList.push({ role: 'assistant', content: this.promoptWords });
        messageList.push({ role: 'user', content: "您好，我已经准备好开始心理测评了,请一个一个的来提问我问题吧!" });
        // messageList.push({ role: 'user', content: "您好" }); // 省 token 的测试代码
      }

      const stream = await this.baiDuAiService.callBaiduAiV2(messageList);
      return new Observable(subscriber => {
        stream.on('data', (chunk) => {
          const data = chunk.toString()
          let content = modelDataDealMethodsMap[createChatDto.model](data)
          assistantTplMessage += content
          subscriber.next(content);
        });

        stream.on('end', async () => {
          const [noTagAssistantTplMessage, isOk] = CheckUserAnswerIsOk(assistantTplMessage)
          if (!isOk) {
            // 报警
          }
          console.log(noTagAssistantTplMessage)
          const audio = await this.genTextAudio(noTagAssistantTplMessage + '') as any
          // 流写入 audioPath
          const audioPath = audio.fileName
          const audioObject = { audioPath: audioPath }
          subscriber.next(`[AUDIO]${JSON.stringify(audioObject)}`);
          subscriber.next("[DONE]");
          subscriber.complete();
          messageList.push({
            role: "assistant",
            content: assistantTplMessage,
            audioPath
          })
          this.redisClient.set(userChatKey, JSON.stringify(messageList));
        });

        stream.on('error', (error) => {
          subscriber.error(`event: error\ndata: ${JSON.stringify({ message: error.message })}\n\n`);
        });
      });
    } catch (e) {
      throw new Error(`event: error\ndata: ${JSON.stringify({ message: e.message })}\n\n`);
    }
  }
  // restartChat 开启一段新的对话

  async restartChat(userId: number) {
    const userChatKey = `userid_${userId}`
    return this.redisClient.del(userChatKey)
  }
  // genTextAudio 文字转语音
  genTextAudio(text: string, voice = 'zh-CN-YunxiaNeural', speed = '1.0') {
    return new Promise(async (resolve) => {
      const ttsHost = this.configService.get('tts')?.host
      const ttsPort = this.configService.get('tts')?.port
      const data = JSON.stringify({
        voice,
        input: text,
        speed
      });
      const config = {
        method: 'post',
        maxBodyLength: Infinity,
        url: `${ttsHost}:${ttsPort}/v1/audio/speech`,
        headers: {
          'Content-Type': 'application/json'
        },
        data,
        responseType: 'arraybuffer' as const,
      };
      try {
        const response = await axios.request(config);
        const timestamp = Date.now();
        const fileName = `speech_${timestamp}.mp3`;
        const filePath = path.join(process.cwd(), 'public', fileName);
        console.log(filePath)
        fs.writeFileSync(filePath, response.data);
        resolve({
          fileName,
          filePath,
          timestamp,
          text,
          voice,
          speed
        })
      } catch (error) {
        console.log('语音生成失败' + error)
        resolve({ error })
      }
    }
    )
  }
}
